﻿
using System.Linq;
using System.Text;
using CatEars.Text.FastFind;

namespace CatEars.Text.Replace
{
    /// <summary>
    /// 提前输入替换词库进行批量替换
    /// </summary>
    public class MultiReplace
    {
        /// <summary>
        /// 快速搜索
        /// </summary>
        protected FastFindSubStr _ffss;
        /// <summary>
        /// 快速搜索忽略库
        /// </summary>
        protected FastFindSubStr _ffssIgnore;

        /// <summary>
        /// 默认替换次数
        /// </summary>
        public int ReplaceTimes { get; set; }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="intDepth">索引深度</param>
        /// <param name="intSubCapacity">内部容器预置容量</param>
        public MultiReplace(
            int intDepth = 2,
            int intSubCapacity = 0)
        {
            _ffss = new FastFindSubStr(intDepth,
                FastFindIndexMode.Asc, FastFindResultTagMode.Single, FastFindResultIndexMode.Start, intSubCapacity);
            _ffssIgnore = new FastFindSubStr(intDepth,
                FastFindIndexMode.Asc, FastFindResultTagMode.None, FastFindResultIndexMode.Start, intSubCapacity);
        }

        #region 标准初始化
        /// <summary>
        /// 初始化词库，添加忽略词
        /// </summary>
        /// <param name="strValue">忽略词</param>
        public void Init_AddIgnore(string strValue)
        {
            if (string.IsNullOrEmpty(strValue))
            {
                return;
            }
            _ffssIgnore.AddValue(strValue);
        }

        /// <summary>
        /// 初始化词库，添加替换词组
        /// </summary>
        /// <param name="strFrom">搜索词</param>
        /// <param name="strTo">替换为</param>
        public void Init_AddReplace(string strFrom, string strTo)
        {
            if (string.IsNullOrEmpty(strFrom))
            {
                return;
            }
            if (string.IsNullOrEmpty(strTo))
            {
                Init_AddRemove(strFrom);
                return;
            }
            else if (strFrom == strTo)
            {
                Init_AddIgnore(strFrom);
            }
            else
            {
                _ffss.AddValue(strFrom, strTo);
            }
        }

        /// <summary>
        /// 初始化词库，添加移除词组
        /// </summary>
        /// <param name="strValue">移除词</param>
        public void Init_AddRemove(string strValue)
        {
            if (string.IsNullOrEmpty(strValue))
            {
                return;
            }
            _ffss.AddValue(strValue, string.Empty);
        }
        #endregion

        #region  从文件初始化
        /// <summary>
        /// 从文本初始化忽略词 '\r', '\n'自动拆分 不需配置
        /// </summary>
        /// <param name="strText">文本</param>
        /// <param name="chrSp">拆分符号 不填默认为 ',', '\t'</param>
        public void Init_IgnoreFromText(string strText, char? chrSp = null)
        {
            if (string.IsNullOrEmpty(strText))
            {
                return;
            }
            string[] sp;
            if (chrSp.HasValue)
            {
                sp = strText.Split(chrSp.Value, '\r', '\n');
            }
            else
            {
                sp = strText.Split(',', '\t', '\r', '\n');
            }
            foreach (var item in sp)
            {
                Init_AddIgnore(item);
            }
        }

        /// <summary>
        /// 从文本初始化
        /// </summary>
        /// <param name="strText">文本</param>
        /// <param name="chrSp">拆分符号 不填默认为 ',', '\t'</param>
        public void Init_FromText(string strText, char? chrSp = null)
        {
            if (string.IsNullOrEmpty(strText))
            {
                return;
            }
            char[] spArr;
            if (chrSp.HasValue)
            {
                spArr = new char[] { chrSp.Value };
            }
            else
            {
                spArr = new char[] { ',', '\t' };
            }
            int intIndex = 0;
            StringBuilder buffer = new StringBuilder();
            while (intIndex < strText.Length)
            {
                char chr = strText[intIndex];
                ++intIndex;
                if (chr == '\r')
                {
                    continue;
                }
                if (chr == '\n')
                {
                    Init_FromText_AddValue(buffer, spArr);
                    buffer.Clear();
                }
                else
                {
                    buffer.Append(chr);
                }
            }
            Init_FromText_AddValue(buffer, spArr);
        }

        /// <summary>
        /// 从文本初始化
        /// </summary>
        /// <param name="buffer">单行文本</param>
        /// <param name="spArr">拆分符号</param>
        private void Init_FromText_AddValue(StringBuilder buffer, char[] spArr)
        {
            string strValue = buffer.ToString();
            var sp = strValue.Split(spArr);
            if (sp.Length >= 2)
            {
                string str1 = sp[0];
                string str2 = sp[1];
                if (str1.Length == 0)
                {
                    return;
                }
                else if (str2.Length == 0)
                {
                    Init_AddRemove(str1);
                }
                else
                {
                    Init_AddReplace(str1, str2);
                }
            }
        }
        #endregion

        /// <summary>
        /// 初始化词库
        /// </summary>
        /// <param name="convMapping">可替换映射词库的文件内容</param>
        /// <param name="stopMapping">不可替换映射词库的文件内容</param>
        /// <param name="chrSp">拆分符号 不填默认为 ',', '\t'</param>
        public void Init(string convMapping, string stopMapping, char? chrSp = null)
        {
            Init_FromText(convMapping, chrSp);
            Init_IgnoreFromText(stopMapping, chrSp);
        }

        /// <summary>
        /// 按照指定词库进行替换
        /// </summary>
        /// <param name="strValue">要替换的字符串</param>
        /// <returns>替换后的字符串</returns>
        public string Replace(string strValue)
        {
            return Replace(strValue, ReplaceTimes);
        }

        /// <summary>
        /// 按照指定词库进行替换
        /// </summary>
        /// <param name="strValue">要替换的字符串</param>
        /// <param name="intTimes">递归处理次数</param>
        /// <returns>替换后的字符串</returns>
        public string Replace(string strValue, int intTimes)
        {
            string strOrginValue = strValue;
            do
            {
                string strResult = Replace(strValue, strOrginValue);
                if (strValue == strResult)
                {
                    break;
                }
                strValue = strResult;
                --intTimes;
            } while (intTimes > 0);
            return strValue;
        }

        /// <summary>
        /// 按照指定词库进行替换
        /// </summary>
        /// <param name="strValue">要替换的字符串</param>
        /// <param name="strOrginValue">批量替换的原始字符串</param>
        /// <returns>替换后的字符串</returns>
        public string Replace(string strValue, string strOrginValue)
        {
            if (string.IsNullOrEmpty(strValue))
            {
                return strValue;
            }
            bool blnChange = false;
            StringBuilder strB = new StringBuilder();
            int i = 0;
        ReplaceStep:
            while (i < strValue.Length)
            {
                //第一步，先从不可转换词表中找不能转换词，若匹配，则不转换。
                var findItem0 = _ffssIgnore.FindStartByIndex(strValue, i, FastFindResultSortMode.MaxValueLength).FirstOrDefault();
                if (findItem0 != null)
                {
                    i += findItem0.Value.Length - 1;
                    strB.Append(findItem0.Value);
                    continue;
                }

                //第二步，从可转换词表中找不能转换词，若匹配，则按词转换。
                var find1 = _ffss.FindStartByIndex(strValue, i, FastFindResultSortMode.ValueLength);
                foreach (var findItem1 in find1)
                {
                    string strOldValue = findItem1.Value;
                    string strNewValue = (string)findItem1.Tags.First();
                    if (!string.IsNullOrEmpty(strNewValue))
                    {
                        int intCheckReplace = BeforeReplace(strOrginValue, strValue, strB, i, strOldValue, strNewValue);
                        if (intCheckReplace < 0)
                        {
                            continue;
                        }
                        if (intCheckReplace == 0)
                        {
                            strB.Append(strNewValue);
                        }
                    }
                    blnChange = true;
                    i += strOldValue.Length;
                    goto ReplaceStep;
                }

                //没有发生替换 正常添加字符
                char chr = strValue[i];
                strB.Append(chr);
                i++;
            }
            if (blnChange) return strB.ToString();
            else return strValue;
        }

        /// <summary>
        /// 判断是否可以进行替换
        /// </summary>
        /// <param name="strOrginValue">原始字符串（重复替换的最原始字符串）</param>
        /// <param name="strValue">处理前字符串</param>
        /// <param name="strBuffer">当前缓存空间</param>
        /// <param name="intIndex">当前匹配位置</param>
        /// <param name="strOldValue">旧值</param>
        /// <param name="strNewValue">新值</param>
        /// <returns>-1不替换 0替换 1不替换也不判断为失败</returns>
        protected virtual int BeforeReplace(
            string strOrginValue,
            string strValue,
            StringBuilder strBuffer,
            int intIndex,
            string strOldValue,
            string strNewValue)
        {
            int intResult = 0;
            if (CheckBreakEnAndNum)
            {
                if (DoCheckBreakEnAndNum(ref intResult, strOrginValue, strValue, strBuffer, intIndex, strOldValue, strNewValue))
                {
                    return intResult;
                }
            }
            return intResult;
        }

        /// <summary>
        /// 防止中英文中间切断
        /// </summary>
        public bool CheckBreakEnAndNum { get; set; }

        #region 替换前检查项
        /// <summary>
        /// 判断是否可以进行替换
        /// </summary>
        /// <param name="intValue">-1不替换 0替换 1不替换也不判断为失败</param>
        /// <param name="strOrginValue">原始字符串（重复替换的最原始字符串）</param>
        /// <param name="strValue">处理前字符串</param>
        /// <param name="strBuffer">当前缓存空间</param>
        /// <param name="intIndex">当前匹配位置</param>
        /// <param name="strOldValue">旧值</param>
        /// <param name="strNewValue">新值</param>
        /// <returns>是否直接返回intValue</returns>
        public static bool DoCheckBreakEnAndNum(
            ref int intValue,
            string strOrginValue,
            string strValue,
            StringBuilder strBuffer,
            int intIndex,
            string strOldValue,
            string strNewValue)
        {
            //主要是拒绝将单词或者数字从中间进行替换
            if (strBuffer.Length > 0)
            {
                //检查前一个字符
                char chr1 = strOldValue[0];
                char chr0 = strBuffer[strBuffer.Length - 1];
                if (((chr1 >= 'a' && chr1 <= 'z') || (chr1 >= 'A' && chr1 <= 'Z') || (chr1 >= '0' && chr1 <= '9'))
                    && ((chr0 >= 'a' && chr0 <= 'z') || (chr0 >= 'A' && chr0 <= 'Z') || (chr0 >= '0' && chr0 <= '9')))
                {
                    intValue = -1;
                    return true;
                }
            }
            if (intIndex + strOldValue.Length < strValue.Length)
            {
                //检查后一个字符
                char chr1 = strOldValue[strOldValue.Length - 1];
                char chr2 = strValue[intIndex + strOldValue.Length];
                if (((chr1 >= 'a' && chr1 <= 'z') || (chr1 >= 'A' && chr1 <= 'Z') || (chr1 >= '0' && chr1 <= '9'))
                    && ((chr2 >= 'a' && chr2 <= 'z') || (chr2 >= 'A' && chr2 <= 'Z') || (chr2 >= '0' && chr2 <= '9')))
                {
                    intValue = -1;
                    return true;
                }
            }
            return false;
        }
        #endregion
    }
}
